package cn.learn.activiti.service.impl;

import cn.learn.activiti.config.SecurityUtil;
import cn.learn.activiti.domain.ActNodeConfig;
import cn.learn.activiti.service.IDeploymentService;
import cn.learn.activiti.service.IHistoricTaskService;
import cn.learn.activiti.service.ITaskService;
import cn.learn.activiti.util.ActThreadLocalContext;
import cn.learn.activiti.util.ActivitiUtils;
import cn.learn.activiti.util.JuelUtil;
import lombok.extern.slf4j.Slf4j;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.model.payloads.ClaimTaskPayload;
import org.activiti.api.task.model.payloads.CompleteTaskPayload;
import org.activiti.api.task.runtime.TaskRuntime;
import org.activiti.bpmn.model.*;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author huangyezhan
 * @version 1.0
 * @date 2021年03月11日18:10
 */
@Slf4j
@Service
public class TaskServiceImpl implements ITaskService {

    @Resource
    private SecurityUtil         securityUtil;
    @Resource
    private TaskRuntime          taskRuntime;
    @Resource
    private TaskService          taskService;
    @Resource
    private IHistoricTaskService historicTaskService;

    @Resource
    private IDeploymentService deploymentService;


    @Override
    public Page<Task> waiting4Task(Integer startIndex, Integer maxItems, String authorization) {
        securityUtil.logInAs(authorization);
        return taskRuntime.tasks(Pageable.of(startIndex, maxItems));
    }

    @Override
    public List<org.activiti.engine.task.Task> getTasksByBusinessKey(String businessKey) {
        return taskService.createTaskQuery()
                .processInstanceBusinessKey(businessKey)
                .list();

    }

    @Override
    public Task completeTask(String taskId, String authorization, ActNodeConfig actNodeConfig) {
        try {
            ActThreadLocalContext.setActNodeConfig(actNodeConfig);

            securityUtil.logInAs(authorization);
            String              num01 = JuelUtil.string2El("num1 > 10");
            String              num02 = JuelUtil.string2El("num2 <= 10");
            Map<String, Object> map   = new HashMap<>(8);
            map.put("num1", 11);
            map.put("num2", 11);

            Map<String, Object> variables    = new HashMap<>(8);
            Set<String>         assigneeList = new HashSet<>();
            assigneeList.add("jerry");
            assigneeList.add("tom");
            variables.put("assigneeList01", assigneeList);
            variables.put("num", 2);

            CompleteTaskPayload build = TaskPayloadBuilder.complete()
                    .withTaskId(taskId)
                    //.withVariable("num01", true)
                    //.withVariable("num02", false)
                    /*.withVariable("num01", 8)
                    .withVariable("user02", "rose")*/
                    .withVariables(variables)
                    .build();

            return taskRuntime.complete(build);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            ActThreadLocalContext.clear();
        }
        return null;
    }

    /**
     * assignee回去授权认证的那个。
     */
    @Override
    public Task claim(String taskId, String authorization) {
        securityUtil.logInAs(authorization);
        ClaimTaskPayload claimTaskPayload = new ClaimTaskPayload(taskId, authorization);
        return taskRuntime.claim(claimTaskPayload);
    }

    @Override
    public void sendBackTask(String taskId) {
        this.assignmentAssignee(taskId, null);
    }

    @Override
    public void assignmentAssignee(String taskId, String userId) {
        taskService.setAssignee(taskId, userId);
    }

    /**
     * 会签的回退还没做校验？？？？？
     * <p/>
     * 不允许回退的情况：
     * ①当前节点是会签
     * ②上一节点是并行网关、包含网关
     * <p/>
     * 回退过程：
     * 1、获取当前任务所在的节点
     * 2、获取所在节点的流出方向
     * 3、记录所在节点的流出方向，并将所在节点的流出方向清空
     * 4、获取目标节点
     * 5、创建新的方向
     * 6、将新的方向set到所在节点的流出方向
     * 7、完成当前任务
     * 8、还原所在节点的流出方向
     * 9、还原上一节点的动态参数，并且设置上一节点的代理人
     */
    @Override
    public void rollbackTask(String currentTaskId) {
        securityUtil.logInAs("rose");
        List<FlowElement> incomeTaskNodes = new ArrayList<>();
        // 当前任务节点
        Task task = taskRuntime.task(currentTaskId);
        if (task == null) {
            log.error("该任务[{}]已处理，或不存在！", currentTaskId);
            throw new IllegalArgumentException("该任务已处理，或不存在！");
        }
        BpmnModel bpmnModel         = deploymentService.getBpmnModel(task.getProcessDefinitionId());
        String    taskDefinitionKey = task.getTaskDefinitionKey();
        FlowNode  currFlow          = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey);
        //本节点 会签不回退，结束
        if (currFlow instanceof UserTask && ActivitiUtils.isMultiInstance((UserTask) currFlow)) {
            log.info("当前任务[{}]是并行多任务节点，不允许回退！", currentTaskId);
            throw new IllegalArgumentException("当前任务[" + currentTaskId + "]是并行多任务节点，不允许回退！");
        }
        //获取上一任务节点
        ActivitiUtils.getIncomeTaskNodesRecur(bpmnModel, incomeTaskNodes, taskDefinitionKey);
        //没获取到上一任务节点
        if (incomeTaskNodes.size() < 1) {
            log.warn("递归没找到任务类型，可能是其他类型节点，请核实");
            throw new IllegalArgumentException("没获取到上一任务节点，请核实！");
        }

        /*
         * 查看上一节点是否符合条件
         * 获取历史走过的节点，如果忽略网关问题，则用List<HistoricTaskInstance>只获取任务就好
         * 判断上一节点是否是：并行网关、包含网关
         */
        List<FlowElement> incomeFlowNodes = new ArrayList<>();
        ActivitiUtils.getIncomeFlowNodeRecur(bpmnModel, incomeFlowNodes, taskDefinitionKey);
        List<HistoricActivityInstance> list          = historicTaskService.getHistoricActivityInstances(task.getProcessInstanceId());
        String                         historyTaskId = null;
        outer:
        for (FlowElement flowNode : incomeFlowNodes) {
            for (HistoricActivityInstance instance : list) {
                //判断上一节点
                if (flowNode.getId().equals(instance.getActivityId())) {
                    FlowElement flowElement = bpmnModel.getFlowElement(instance.getActivityId());
                    //网关判断
                    if (flowElement instanceof ParallelGateway || flowElement instanceof InclusiveGateway) {
                        throw new IllegalArgumentException("上一节点同时下发任务给多人，不能回退");
                    }
                    historyTaskId = instance.getTaskId();
                    //上一节点的 会签判断
                    /*if (flowElement instanceof UserTask && ActivitiUtils.isMultiInstance((UserTask) flowElement)) {
                        log.info("上一节任务[{}]是并行多任务节点，不允许回退！", currentTaskId);
                        throw new IllegalArgumentException("当前任务[" + currentTaskId + "]是并行多任务节点，不允许回退！");
                    }*/
                    break outer;
                }
            }
        }

        // 记录原活动方向
        List<SequenceFlow> oriSequenceFlows = new ArrayList<>(currFlow.getOutgoingFlows());
        //清理活动方向
        currFlow.getOutgoingFlows().clear();

        //获取新的活动方向
        List<SequenceFlow> newSequenceFlows = new ArrayList<>();
        for (FlowElement flowElement : incomeTaskNodes) {
            // 获取目标节点
            FlowNode target = (FlowNode) flowElement;
            //如果不是同一个流程（子流程）不能驳回
            if (!(currFlow.getParentContainer().equals(target.getParentContainer()))) {
                continue;
            }
            // 建立新方向
            SequenceFlow newSequenceFlow = new SequenceFlow();
            String       uuid            = UUID.randomUUID().toString().replace("-", "");
            newSequenceFlow.setId(uuid);
            // 原节点
            newSequenceFlow.setSourceFlowElement(currFlow);
            // 目标节点
            newSequenceFlow.setTargetFlowElement(target);
            newSequenceFlows.add(newSequenceFlow);
        }
        currFlow.setOutgoingFlows(newSequenceFlows);
        // 拒接、通过、驳回指定节点
        taskService.complete(currentTaskId);
        // 恢复原方向
        currFlow.setOutgoingFlows(oriSequenceFlows);

        //还原上一节点的动态参数，并且设置上一节点的代理人
        List<HistoricVariableInstance> variables = historicTaskService.getHistoryVariablesByTaskId(historyTaskId);


    }

    /**
     * 如果下一节点已经完成任务了，则不能做撤回操作
     * 撤回过程:
     * 1、获取指定的历史任务
     * 2、获取该历史节点的下一节点
     * 3、获取当前运行中的任务，判断有没有包含下一节任务节点在里面
     * 4、下一节点的所有任务节点都未完成推向再下一节点
     * 5、撤回操作
     *
     * @param currentHistoryTaskId 当前的历史任务ID
     */
    @Override
    public void withdrawTask(String currentHistoryTaskId) {
        HistoricTaskInstance historyTask = historicTaskService.getHistoryTaskById(currentHistoryTaskId);
        if (historyTask == null) {
            log.warn("不存在该历史任务[{}]，请确认！", currentHistoryTaskId);
            throw new IllegalArgumentException("不存在该历史任务，请确认！");
        }
        String username = "";
        if (!historyTask.getAssignee().equals(username)) {
            log.warn("该任务[{}]，不是当前用户提交的，不能撤回！", currentHistoryTaskId);
            throw new IllegalArgumentException("该任务，不是当前用户提交的，不能撤回！");
        }
        //获取下一任务节点
        List<FlowElement> incomeTaskNodes = new ArrayList<>();
        BpmnModel         bpmnModel       = deploymentService.getBpmnModel(historyTask.getProcessDefinitionId());
        ActivitiUtils.getIncomeTaskNodesRecur(bpmnModel, incomeTaskNodes, historyTask.getTaskDefinitionKey());
        List<String> collect = incomeTaskNodes.stream().map(FlowElement::getId).collect(Collectors.toList());

        //查看下一任务的节点在历史任务里面，是否完成了
        List<HistoricTaskInstance> historyTasks = historicTaskService.getHistoryTaskByBusinessKey(historyTask.getBusinessKey());
        for (HistoricTaskInstance task : historyTasks) {
            if (task.getEndTime() != null) {
                String taskDefinitionKey = task.getTaskDefinitionKey();
                if (collect.contains(taskDefinitionKey)) {
                    log.warn("该任务[{}]不能撤回，下一任务已有完成的了！", currentHistoryTaskId);
                    throw new IllegalArgumentException("该任务不能撤回，下一任务已有完成的了！");
                }
            }
        }
    }


}
